package com.tedu.api.io;

import org.junit.Test;

import java.io.*;
import java.nio.charset.StandardCharsets;

/**
 * IO流：传输数据
 * File文件、文件夹类：无法读写数据
 * RandomAccessFile：读写按照字节，读写类不明确，靠构造器传入第二个参数
 *
 * Java 提出一套标准IO--读写单独提供类
 * 1.方向区别：
 * 输入流---（读：针对文件，文件往程序中走，文件“相对于程序”）
 * 输出流--（写：针对文件，文件从程序写出，文件“相对于程序”）
 * 输入流和输出流创建方式
 * 输入流：   InputStream（抽象类）--顶级父类--读操作
 * 输出流     OutputStream（抽象类）--顶级父类--写操作
 * 以上两个流定义所有流具备读写操作，本身无法实现实例化
 * 2.按照功能进行区分
 * 低级流：专门搬运数据的流【叫做节点流】
 * FileInputStream   文件输入流：读操作-----InputStream（抽象类）
 * FileOutputStream  文件输出流：写操作-----OutputStream（抽象类）
 * 功能：建立在程序与数据源之间的“管道”所有数据传输通过低级流【节点流】
 *高级流：不能独立存在的流【叫做处理流】
 *       处理低级流的数据，并且可以被其他高级流所处理
 *       理解：处理搬运数据的流【高级流种类比较多】
 * -------------流：明确功能，创建对象：直接使用低级流--高级流创建
 * 项目：聊天室   Version 1      IO流：针对传字符串
 */
public class IOAPI {
    @Test
    public void testIO() throws IOException {
        //创建标准的IO流，方向划分：输入流与输出流--功能：低级流【节点流】
        //需求：从项目中写出“fos.txt”  低级流：FileOutputStream
        //创建文件输出流 ---  构造器（“相对路径+文件全称”）
        // 抛出异常FileNotFoundException   没有文件的异常
        //原因：路径或者文件名写错
        FileOutputStream fos=new FileOutputStream("fos.txt");
        /**
         * 输出一句话：   我门市祖国的花朵
         * write(字符串)   返回值：void
         * 作用：写出以后个文件内容：字节数组
         * 抛出异常  IOException  IO流异常：写出失败
         */
        //String 字符串 方法：getBytes（）  可以将字符串转换成字符数组  char[]
        fos.write("我们是祖国的花朵".getBytes());
        //标准IO流必须关闭
        fos.close();//释放流：避免占用内存
    }
    /**FileOutputStream实现类---构造器使用方法
     * new  FileOutputStream（String path,boolean append）
     * 通过第二个参数，确定数据是否追加，trye代表追加，反之
     * new  FileOutputStream（File file,boolean append）
     */
   // @Test
   // public void testIOA() throws IOException {
        //当前API 项目 下添加fos.txt--增加功能：；追加效果
        //相对路径：  ./fos.txt    直接写文件|不屑./  都是当前目录下
        ///FileOutputStream fos=new FileOutputStream("fos.txt",true);
       //String str="是未来~";
       // fos.write(str.getBytes());
        //关闭流
       // fos.close();
    //}
    /**
     * 低级流
     * 文件输入流
     * FileInputStream
     * read（）  返回值：int
     * 作用：读取到一个字节
     */
   // @Test
   // public void testInputStream() throws IOException {
        //创建文件输入流-----读入
        //RandomAccessFile raf=new RandomAccessFile(("fos.txt","r"));
      //  FileInputStream fis=null;
        //BufferedOutputStream bos=null;

       // FileInputStream fis= null;
       // try {
          //  fis = new FileInputStream("fos.txt");
       // } catch (FileNotFoundException e) {
       //     e.printStackTrace();
      //  } finally {
      //  }
        //10KB  字节数组
        //byte[] bytes=new byte[1024*10];
        //10KB 读 数据   【不想看到整数值】
        //int len=fis.read(bytes);//len返回-1  文件读取完毕
        //String 字符串类--提供构造器方法：按照字节数组量，输出字符串
        //String 构造器 new String(自己额数组，开始读取文件，结束读取文件)；
        //将【二进制值】整数 转换为 字符串
        //String str=new String(bytes,0,len);//循环
        //一次性激昂给定的字节数组【10KB】写入到文件当中，文件从0开始写入，到-1结束，写入完毕
        //System.out.println("打印读："+str);
        //fis.close();
   // }
    //需求：【标准IO】重构RandomAccessFile类--复制视频的方法
    @Test
    public void testWork() throws IOException {
        //1.创建输入流---读---被复制的文件【源文件】
        FileInputStream fis=new FileInputStream("复制文件.mp4");
        //2.创建输出流---写---复制品
        FileOutputStream fos=new FileOutputStream("标准IO.mp4");
        //3.用于保存读取到的字节量
        int len=-1;//①保存读取的数据值【可以被覆盖】②-1代表吻技安结尾
        //6.视频空间大，解决方案：10KB，字节数组：设定保存值10KB
        byte[] bytes=new byte[1024*10];
        //4.一直读取【10KB】文件，保存len中，如果结果不等于-，一直读
        //(len=fis.read(bytes))!=-1
        long start=System.currentTimeMillis();//毫秒数
        while((len=fis.read(bytes))!=-1){
            //4.1将读取到的【10KB】文件写出
            fos.write(bytes,0,len);
        }
        long end=System.currentTimeMillis();//结束毫秒数
        System.out.println("测试传输时间："+(end-start)/1000+"秒");
        //5.关闭流
        fis.close();
        fos.close();
    }
    /**
     * 高级流【处理流】：处理低级流以及其他高级流的一种流
     * 高级流不能独立存在，一定处理低级流
     * BufferedInputStream---高级流：快速读   缓冲输出流
     * BufferedOutputStream--高级流：快速写   缓冲输入流
     * 以上两个该机六，内嵌一套按照实际传输数据大小，随机型改变的字节数组
     * 高级流语法：
     * BufferedInputStream bis=new BufferedInputStream(低级流引用):
     * 需求：视频 使用IO流【低级流+高级流】--实现视频快速复制
     */
    @Test
    public void testCV() throws IOException {
        //创建低级流：输入流:高级流、输出流：高级流
        FileInputStream fis=new FileInputStream("复制文件.mp4");
        BufferedInputStream bis=new BufferedInputStream(fis);
        FileOutputStream fos=new FileOutputStream("缓冲文件.mp4");
        BufferedOutputStream bos=new BufferedOutputStream(fos);
        //2.定义整数变量保存每次读取的字节两
        int len=-1;
        //3.将高级流：缓冲输入流--读取的数据--保存交给len，结果不等于-1
        //(len=bis.read())!=-1
        long start=System.currentTimeMillis();
        while((len=bis.read())!=-1){
            bos.write(len);
        }
        long end=System.currentTimeMillis();
        System.out.println("缓冲花费时间："+(end-start));
        //4.关闭流
        fis.close();
        fos.close();
    }
    /**
     * 【高级流：处理流】
     * BufferedOutputStream 缓冲输出流
     * 需求：写出一份文件：bos.txt,写出：一段文本内容
     * 所有的流，全部都不是正确的西俄法：IO流异常最多，采取捕获异常并作出处理
     */
    @Test
    public void testBOS() throws IOException {
        //创建低级流--写出bos.txt
        FileOutputStream fos=new FileOutputStream("bos.txt");
        //创建高级流--写出一段文本内容【缓冲区：字节数组】
        BufferedOutputStream bos=new BufferedOutputStream(fos);
        String str="50块钱再好看，也不如100块钱招人稀罕";
        //write(byte[] b)   作用：写出字节数组
        bos.write(str.getBytes());
        /**
         * str 数据量小      缓冲区大     强制送出【flush】    被动送出【closed】
         * flush()
         * 作用:可以强制将内部缓冲区数据一次性读写
         * 缺点：频繁使用flush降低读写
         */
        //bos.flush();

        try {
            fos.close();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (bos!=null)
                try {
                    bos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
        }
    }
    /**
     * 异常大小问题：
     * IO流：FileNotFoundException>IOException
     *      Exception顶级父类
     *      ------------------------------------------------
     *
     *      低级流：FileOutputStream  文件输出流---写出
     *             FileInputStream   文件输入流---读入
     *      高级流：BufferedOutputStream  缓冲输出流---写出---功能：缓冲区
     *             BufferedInOutputStream 缓冲输入流--读入--功能：缓冲区
     *
     * 考试题:final与finally的区别?
     * final关键字:修饰变量、方法、类-->变量成常量
     * 方法不能重写
     * 类不能被继承
     * finally关键字:异常处理机制中:捕获异常
     * finally在捕获异常中， 可以写，，也可以不写
     * 所有异常都不符和，一定会执行的语法块
     * 写finally关键字所有异常都不符和，一定会执行的语法块
     * 通常使用在IO流中:关闭流为主!关闭高级流-->间接关闭低级流
     */
    @Test
    public void testBOSIO() throws IOException {
//1.需要将流置空:置空--①覆盖，不用多次创建对象!②关闭流
        FileOutputStream fos = null;//低级流--搬运数据:写出
        BufferedOutputStream bos = null;//高级流--处理低级流:缓冲输出流
        //2.捕获异常操作:alt+ctrl+T
        //2.1 创建输出流
        fos = new FileOutputStream("BOS标准流写法.txt");
        bos = new BufferedOutputStream(fos);
        //3.高级流:缓冲输出流---功能:缓冲[字节数组]写出数据
        bos.write("标准流写法".getBytes());
        //4.缓冲区:数据量比较小---采用flush()
try{


            bos.flush();
        } catch (FileNotFoundException e) {//文件不存在的异常:路径写错
            e.printStackTrace();//打印异常信息
            System.out.println("【小】文件不存在异常");
        } catch (IOException e) {//IO流最大的异常：小异常不处理
            e.printStackTrace();
            System.out.println("【中】IO流异常");
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("【大】异常");
        } finally {
    //一定执行代码块：IO流关闭--高级流--关闭高级流--低级流关闭【捕获异常】
    //                        低级流--关闭低级流【捕获异常】
//5.先判断，高级流--是否为空【有没有高级流对象】--有高级流对象【必须关闭】
    //                                 没有高级流对象【不必关闭】
    if (bos!=null)
        try {
            bos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        }
    }
}

